// $Id: hex_format.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_DETAIL_HEX_FORMAT_HPP
#define XPCOM_DETAIL_HEX_FORMAT_HPP

#include <xpcom/config.hpp>
#include <xpcom/types.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {
namespace detail {

inline const char* hex_tab_lower()
{
    static const char* tab = "0123456789abcdef";
    return tab;
}

inline const char* hex_tab_upper()
{
    static const char* tab = "0123456789ABCDEF";
    return tab;
}

inline int hex_parse_nibble(int c)
{
    return c >= 'A' ? (c | 0x20) - 'a' + 10 : c - '0';
}

template <typename CharT>
const CharT* hex_read(const CharT* s, uint8_t& val)
{
    int a = hex_parse_nibble(*s++);
    int b = hex_parse_nibble(*s++);
    val = static_cast<uint8_t>(a << 4 | b);
    return s;
}

template <typename CharT>
const CharT* hex_read(const CharT* s, uint16_t& val)
{
    uint8_t* p = reinterpret_cast<uint8_t*>(&val);

#if defined(XPCOM_LITTLE_ENDIAN) || defined(XPCOM_PDP_ENDIAN)
    s = hex_read(s, p[1]);
    s = hex_read(s, p[0]);
#elif defined(XPCOM_BIG_ENDIAN)
    s = hex_read(s, p[0]);
    s = hex_read(s, p[1]);
#else
#  error setup endian type for your CPU please.
#endif

    return s;
}

template <typename CharT>
const CharT* hex_read(const CharT* s, uint32_t& val)
{
    uint8_t* p = reinterpret_cast<uint8_t*>(&val);

#if defined(XPCOM_LITTLE_ENDIAN)
    s = hex_read(s, p[3]);
    s = hex_read(s, p[2]);
    s = hex_read(s, p[1]);
    s = hex_read(s, p[0]);
#elif defined(XPCOM_BIG_ENDIAN)
    s = hex_read(s, p[0]);
    s = hex_read(s, p[1]);
    s = hex_read(s, p[2]);
    s = hex_read(s, p[3]);
#elif defined(XPCOM_PDP_ENDIAN)
    s = hex_read(s, p[1]);
    s = hex_read(s, p[0]);
    s = hex_read(s, p[3]);
    s = hex_read(s, p[2]);
#else
#  error setup endian type for your CPU please.
#endif

    return s;
}

template <typename CharT>
CharT* hex_write(CharT* s, uint8_t val, const char* hex_tab)
{
    *s++ = hex_tab[val >> 4];
    *s++ = hex_tab[val & 0x0F];
    return s;
}

template <typename CharT>
CharT* hex_write(CharT* s, uint16_t val, const char* hex_tab)
{
    const uint8_t* p = reinterpret_cast<const uint8_t*>(&val);

#if defined(XPCOM_LITTLE_ENDIAN) || defined(XPCOM_PDP_ENDIAN)
    s = hex_write(s, p[1], hex_tab);
    s = hex_write(s, p[0], hex_tab);
#elif defined(XPCOM_BIG_ENDIAN)
    s = hex_write(s, p[0], hex_tab);
    s = hex_write(s, p[1], hex_tab);
#else
#  error setup endian type for your CPU please.
#endif

    return s;
}

template <typename CharT>
CharT* hex_write(CharT* s, uint32_t val, const char* hex_tab)
{
    const uint8_t* p = reinterpret_cast<const uint8_t*>(&val);

#if defined(XPCOM_LITTLE_ENDIAN)
    s = hex_write(s, p[3], hex_tab);
    s = hex_write(s, p[2], hex_tab);
    s = hex_write(s, p[1], hex_tab);
    s = hex_write(s, p[0], hex_tab);
#elif defined(XPCOM_BIG_ENDIAN)
    s = hex_write(s, p[0], hex_tab);
    s = hex_write(s, p[1], hex_tab);
    s = hex_write(s, p[2], hex_tab);
    s = hex_write(s, p[3], hex_tab);
#elif defined(XPCOM_PDP_ENDIAN)
    s = hex_write(s, p[1], hex_tab);
    s = hex_write(s, p[0], hex_tab);
    s = hex_write(s, p[3], hex_tab);
    s = hex_write(s, p[2], hex_tab);
#else
#  error setup endian type for your CPU please.
#endif

    return s;
}

template <typename CharT>
inline CharT* hex_write_lower(CharT* s, uint8_t val)
{
    return hex_write(s, val, hex_tab_lower());
}

template <typename CharT>
inline CharT* hex_write_upper(CharT* s, uint8_t val)
{
    return hex_write(s, val, hex_tab_upper());
}

template <typename CharT>
inline CharT* hex_write_lower(CharT* s, uint16_t val)
{
    return hex_write(s, val, hex_tab_lower());
}

template <typename CharT>
inline CharT* hex_write_upper(CharT* s, uint16_t val)
{
    return hex_write(s, val, hex_tab_upper());
}

template <typename CharT>
inline CharT* hex_write_lower(CharT* s, uint32_t val)
{
    return hex_write(s, val, hex_tab_lower());
}

template <typename CharT>
inline CharT* hex_write_upper(CharT* s, uint32_t val)
{
    return hex_write(s, val, hex_tab_upper());
}

} // namespace xpcom::detail
} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_DETAIL_HEX_FORMAT_HPP
